package server.connector.http;

import server.Container;
import server.logger.Logger;

import java.io.IOException;
import java.net.BindException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.*;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;

public class HttpConnector implements Runnable {

    private Container container;
    //连接器线程名
    private String threadName = null;
    private Logger logger;

    private boolean initialized = false;
    //服务器套接字
    private ServerSocket serverSocket;
    //输入流缓冲区大小
    private int bufferSize = Constants.DEFAULT_INPUT_STREAM_BUFFER_SIZE;

    private int proxyPort = 0;
    private String proxyName = null;
    //安全连接?
    private boolean secure = false;
    //使用的协议
    private String scheme = "http";
    private boolean allowChunking = true;
    private boolean enableLookups = false;
    //创建请求对象
    public HttpRequestImpl createRequest() {
        HttpRequestImpl request = new HttpRequestImpl();
        request.setConnector(this);
        return request;
    }

    //创建响应对象
    public HttpResponseImpl createResponse() {
        HttpResponseImpl response = new HttpResponseImpl();
        response.setConnector(this);
        return response;
    }

    //初始化线程名
    //初始化日志器
    //打开ServerSocket
    //失败抛异常
    public void initialize() throws Exception {
        if (initialized)
            throw new Exception("连接器已经初始化过");

        threadName = "HttpConnector[" + port + "]";
        logger = Logger.getLogger(threadName);
        initialized = true;

        try {
            serverSocket = openServerSocket();
        } catch (Exception e) {
            logger.log("连接器初始化错误: ", e);
            throw new Exception(threadName + ".openServerSocket" + e);
        }
    }

    //端口号
    private int port = Constants.DEFAULT_PORT;
    //连接队列长度
    private int acceptCount = Constants.DEFAULT_ACCEPT_COUNT;
    //服务器IP地址,供主机有多个IP地址时选择,为nullServerSocket会监听主机上的所有地址
    private String address = null;

    //根据port,acceptCount,address创建ServerSocket
    //成功返回ServerSocket对象，失败报异常
    private ServerSocket openServerSocket() throws Exception {
        //所有地址都绑定
        if (address == null) {
            try {
                return new ServerSocket(port, acceptCount);
            } catch (BindException e) {
                throw new BindException(e.getMessage() + ":" + port);
            }
        }
        //绑定指定地址
        try {
            InetAddress inetAddress = InetAddress.getByName(address);
            try {
                return new ServerSocket(port, acceptCount, inetAddress);
            } catch (BindException e) {
                throw new BindException(e.getMessage() + ":" + address + ":" + port);
            }
        } catch (Exception e) {
            //绑指定地址失败，绑全部地址
            try {
                return new ServerSocket(port, acceptCount);
            } catch (BindException be) {
                throw new BindException(be.getMessage() + ":" + port);
            }
        }
    }

    //是否启动过
    private boolean started = false;

    //最少HTTP处理器数量
    protected int minProcessors = Constants.DEFAULT_MIN_PROCESSORS;
    //最大HTTP处理器数量 为负数时表示没有数量限制
    private int maxProcessors = Constants.DEFAULT_MAX_PROCESSORS;
    //当前HTTP处理器数量
    private int curProcessors = 0;
    //存放已经创建但未使用的HTTP处理器
    private Deque<HttpProcessor> processors = new LinkedList<>();
    private ArrayList<HttpProcessor> created = new ArrayList<>();
    public void start() throws Exception {
        if (started)
            throw new Exception("连接器已经启动过");
        started = true;

        //创建最初需要的处理器数放到栈中,并启动这些线程
        while (curProcessors < minProcessors) {
            //特殊情况，最少处理器数比最大处理器数多
            if(maxProcessors > 0 && curProcessors >= maxProcessors)
                break;
            reuseProcessor(newAndStartProcessor());
        }

        threadStart();

    }
    //创建并启动一个新的处理器
    private HttpProcessor newAndStartProcessor() {
        HttpProcessor processor = new HttpProcessor(this, curProcessors++);
        try {
            //启动处理器自己的线程
            processor.start();
        } catch (Exception e) {
            logger.log("启动处理器失败");
            return null;
        }

        created.add(processor);
        return processor;
    }

    //回收处理器重复使用
    void reuseProcessor(HttpProcessor processor) {
        //多个处理器线程都可能调用,要加锁
        synchronized (processors) {
            processors.push(processor);
        }
    }

    //连接器线程
    private Thread thread = null;
    //启动连接器自己的线程
    private void threadStart() {
        thread = new Thread(this, threadName);
        //设置为守护线程
        thread.setDaemon(true);
        thread.start();
    }
    //getter-------------------------------------------------------------------

    public int getPort() {
        return port;
    }
    public int getBufferSize() {
        return bufferSize;
    }
    public String getProxyName() {
        return this.proxyName;
    }
    public int getProxyPort() {
        return this.proxyPort;
    }
    public boolean isSecure() {
        return this.secure;
    }
    public String getScheme() {
        return this.scheme;
    }
    public boolean isChunkingAllowed() {
        return this.allowChunking;
    }
    public Container getContainer() {
        return this.container;
    }

    public boolean isEnableLookups() {
        return this.enableLookups;
    }
    //获取实际服务器的主机名
    public String getLocalName() {
        return serverSocket.getInetAddress().getHostName();
    }
    //获取实际服务器的IP地址
    public String getLocalAddr() {
        return serverSocket.getInetAddress().getHostAddress();
    }
    //获取实际服务器的端口号
    public int getLocalPort() {
        return serverSocket.getLocalPort();
    }

    //setter-------------------------------------------------------------------
    public void setContainer(Container container) {
        this.container = container;
    }

    //实现Runnable接口-------------------------------------------------------------------
    private boolean stopped = false;
    private int connectionTimeout = Constants.DEFAULT_CONNECTION_TIMEOUT;
    private boolean tcpNoDelay = true;
    private Object threadSync = new Object();
    @Override
    public void run() {
        logger.log("连接器初始化成功,正在监听");
        while (!stopped) {
            Socket socket = null;
            try {
                socket = serverSocket.accept();
                if (connectionTimeout > 0) {
                    //设置阻塞时间,read阻塞时间超出该值时,会报java.net.SocketTimeoutException异常
                    //单位为毫秒
                    socket.setSoTimeout(connectionTimeout);
                }
                //如果Nagle算法关闭,对于小数据包,比如一次鼠标移动,点击,客户端都会立马和服务端交互，实时响应度非常高，但频繁的通信却很占用不少网络资源。
                //如果Nagle算法开启,算法会自动合并小数据包,等到达到一定大小（MSS）后,才会和服务端交互,优点是减少了通信次数,缺点是实时响应度会低一些。
                //Socket创建时,默认是开启Nagle算法的,可以根据实时性要求来选择是否关闭Nagle算法。
                //当前选择关闭Nagle算法
                socket.setTcpNoDelay(tcpNoDelay);
            } catch (AccessControlException ace) {
                //public static void checkPermission(Permission perm) throws AccessControlException
                //调用accept方法可能会出现该异常,连接失败,等待下一个连接(应该只是连接失败,serverSocket没问题)
                logger.log("socket accept security exception", ace);
                continue;
            } catch (IOException e) {
                //其他异常(应该是serverSocket出现问题,要重新打开)
                try {
                    //不懂为啥加锁
                    synchronized (threadSync) {
                        if (started && !stopped) {
                            logger.log("accept error: ", e);
                        }
                        if (!stopped) {
                            serverSocket.close();
                            serverSocket = openServerSocket();
                        }
                    }
                } catch (Exception ex) {
                    //重新打开过程中出错,跳出循环
                    logger.log("服务器套接字重新打开失败:", ex);
                    break;
                }
                //重新打开成功,重新开始等待连接
                continue;
            }
            //建立连接成功，开始处理连接

            //获取处理器
            HttpProcessor processor = getProcessor();
            //处理器个数达到最大数,没用可用的处理器了,直接关闭连接
            if (processor == null) {
                try {
                    logger.log("当前无可用处理器,直接关闭连接");
                    socket.close();
                } catch (Exception e) {

                }
                //等待下次连接
                continue;
            }

            //将processor唤醒,处理请求
            processor.putSocket(socket);
        }
        //不懂
        synchronized (threadSync) {
            threadSync.notifyAll();
        }
    }
    //当栈中有处理器可用时，从栈里拿，否则若可以再创建新处理器，则创建返回，否则返回null
    private HttpProcessor getProcessor() {
        ////连接器拿时处理器可能往里放,要加锁
        synchronized (processors) {
            //栈里的处理器没用完,直接拿来用
            if (processors.size() > 0) {
                return processors.pop();
            }
            //限制最大处理器数并且当前未达到最大数,新创建一个处理器
            if (maxProcessors > 0 && curProcessors < maxProcessors) {
                return newAndStartProcessor();
            } else {
                //无限制创建处理器数
                if (maxProcessors < 0)
                    return newAndStartProcessor();
                else
                    //有限制创建且达到最大限制
                    return null;
            }
        }
    }
}
